package com.symone.webcms.service.impl;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import com.symone.webcms.config.GlobalConfig;
import com.symone.webcms.model.base.ResultMessage;
import com.symone.webcms.model.vo.FileVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class FileUploadService {

    private Logger logger = LoggerFactory.getLogger(FileUploadService.class);
    private String IMAGE_TYPE_LIST[] = {"bmp", "jpg", "jpeg", "png", "tiff", "gif", "pcx", "tga", "svg", "cdr", "dxf", "eps", "ai", "wmf"};
    private String VIDEO_TYPE_LIST[] = {"mpeg","mpg","mpv","mpv2","mpe","mpga","mp4","wmf", "avi", "wmv", "asf", "mpa", "rmvb"};
    private String DOCUMENT_TYPE_LIST[] = {"txt", "doc", "docx", "xls", "htm", "html","rtf", "wpd", "pdf", "ppt"};

    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    public ResultMessage localUpload(MultipartFile file, HttpServletRequest request ,Integer width, Integer height) throws IOException {
        //上传文件路径
        String path = ResourceUtils.getURL("classpath:").getPath();

        String localFileName = file.getOriginalFilename();
        String fileType = localFileName.substring(localFileName.lastIndexOf(".") + 1, localFileName.length()).toLowerCase();

        if (Arrays.asList(IMAGE_TYPE_LIST).contains(fileType) && width != null && height != null ) {
            if( !checkImageSize(file,width,height)){
                return ResultMessage.fail("文件上传失败!图片尺寸大小不符！");
            }
        }

        String serverFileName = "/";
        serverFileName = switchFileType(serverFileName,fileType);
        String fileUrl = path + serverFileName;

        File filepath = new File(fileUrl);
        //判断路径是否存在，如果不存在就创建一个
        if (!filepath.getParentFile().exists()) {
            filepath.getParentFile().mkdirs();
        }
        //将上传文件保存到一个目标文件当中
        file.transferTo(filepath);

        String url = "http://"+request.getServerName()+ ":"+ request.getServerPort()+"/";
        return ResultMessage.success("文件上传成功",url + serverFileName);
    }

    private boolean checkImageSize(MultipartFile file,int width,int height) {
        try {
            BufferedImage image = ImageIO.read(file.getInputStream());
            if (image != null) {
                if(image.getWidth() == width && image.getHeight() == height){
                    return true;
                }
            }
        } catch (IOException e) {
            return false;
        }
        return false;
    }

    public String aliyunUpload(MultipartFile file) throws IOException {
        String endpoint = GlobalConfig.getInstance().get("sys_aliyun_oss_endpoint");
        String accessKeyId = GlobalConfig.getInstance().get("sys_aliyun_oss_keyid");
        String accessKeySecret = GlobalConfig.getInstance().get("sys_aliyun_oss_keysecret");
        String bucketName = GlobalConfig.getInstance().get("sys_aliyun_oss_bucketname");
        String fileHost = GlobalConfig.getInstance().get("sys_aliyun_oss_filehost");

        String dateStr = format.format(new Date());
        //创建ossClient实例
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        //判断bucket是否存在
        //容器不存在，就创建
        if (!ossClient.doesBucketExist(bucketName)) {
            ossClient.createBucket(bucketName);
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
            ossClient.createBucket(createBucketRequest);
        }

        String originName = file.getOriginalFilename();
        String fileType = originName.substring(originName.lastIndexOf(".") + 1, originName.length()).toLowerCase();
        String fileUrl = fileHost + "/";
        fileUrl = switchFileType(fileUrl,fileType);
        //创建文件路径
        logger.info(fileUrl);
        ossClient.putObject(bucketName, fileUrl, file.getInputStream(), getAliyunObjectMetadata(fileType));
        ossClient.shutdown();
        return fileUrl;
    }

    @Cacheable(value = "imgurl",key = "'access_file'+#p0")
    public String accessFile(String key){
        String endpoint = GlobalConfig.getInstance().get("sys_aliyun_oss_endpoint");
        String accessKeyId = GlobalConfig.getInstance().get("sys_aliyun_oss_keyid");
        String accessKeySecret = GlobalConfig.getInstance().get("sys_aliyun_oss_keysecret");
        String bucketName = GlobalConfig.getInstance().get("sys_aliyun_oss_bucketname");
        String fileHost = GlobalConfig.getInstance().get("sys_aliyun_oss_filehost");
        //创建ossClient实例
        OSSClient ossClient=new OSSClient(endpoint,accessKeyId,accessKeySecret);

        //生成签名URL(替代new Date().getTime() 更加精确)
        Date expiration=new Date(System.currentTimeMillis()+3600*1000);
        URL url=ossClient.generatePresignedUrl(bucketName,key,expiration);

        ossClient.shutdown();
        return url.toString();
    }
    /**
     * 删除文件
     *
     * @param fileUrl
     * @return
     */
    public boolean delete(String fileUrl) {
        logger.info("删除文件["+ fileUrl +"]开始 ...");
        try {
            String path = ResourceUtils.getURL("classpath:").getPath();
            File file = new File(path + fileUrl);
            if (file.exists() && file.isFile()) {
                if (file.delete()) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } catch (Exception ex) {
            logger.info("删除文件失败", ex);
            return false;
        }
    }

    /**
     * 删除Object
     *
     * @param fileUrl
     * @return
     */
    public boolean deleteOSS(String fileUrl) {
        logger.info("=========>OSS文件删除开始");
        String endpoint = GlobalConfig.getInstance().get("sys_aliyun_oss_endpoint");
        String accessKeyId = GlobalConfig.getInstance().get("sys_aliyun_oss_keyid");
        String accessKeySecret = GlobalConfig.getInstance().get("sys_aliyun_oss_keysecret");
        String bucketName = GlobalConfig.getInstance().get("sys_aliyun_oss_bucketname");
        String fileHost = GlobalConfig.getInstance().get("sys_aliyun_oss_filehost");
        try {
            OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

            if (!ossClient.doesBucketExist(bucketName)) {
                logger.info("==============>您的Bucket不存在");
                return false;
            } else {
                logger.info("==============>开始删除Object");
                ossClient.deleteObject(bucketName, fileUrl);
                logger.info("==============>Object删除成功：" + fileUrl);
                return true;
            }
        } catch (Exception ex) {
            logger.info("删除Object失败", ex);
            return false;
        }
    }

    /**
     * 查询文件名列表
     *
     * @param bucketName
     * @return
     */
    public List<FileVO> getImageList(String bucketName, String dir) {
        String imgPath = (dir != null && dir.trim().length() > 0 ) ? "images/" + dir : "images";
        return getOSSList(bucketName, imgPath);
    }

    /**
     * 查询文件名列表
     *
     * @param bucketName
     * @return
     */
    public List<FileVO> getVideoList(String bucketName,String dir) {
        String vodPath = (dir != null && dir.trim().length() > 0 ) ? "videos/" + dir : "videos";
        return getOSSList(bucketName, vodPath);
    }
    /**
     * 查询文件名列表
     *
     * @param bucketName
     * @param dir
     * @return
     */
    public List<FileVO> getOSSList(String bucketName, String dir) {
        List<FileVO> listRe = new ArrayList<>();
        String endpoint = GlobalConfig.getInstance().get("sys_aliyun_oss_endpoint");
        String accessKeyId = GlobalConfig.getInstance().get("sys_aliyun_oss_keyid");
        String accessKeySecret = GlobalConfig.getInstance().get("sys_aliyun_oss_keysecret");
        String fileHost = GlobalConfig.getInstance().get("sys_aliyun_oss_filehost");
        try {
            logger.info("===========>查询文件名列表");
            OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

            int maxKeys = 1000;
            String keyPrefix ="";
            String nextMarker = "";
            ObjectListing objectListing;

            //列出目录下今天所有文件
            if(dir != null && dir.trim().length() > 0) {
                keyPrefix = fileHost + "/" + dir + "/";
            }else{
                keyPrefix = fileHost + "/";
            }
            do{
                objectListing = ossClient.listObjects(new ListObjectsRequest(bucketName).
                        withPrefix(keyPrefix).withMarker(nextMarker).withMaxKeys(maxKeys));
                for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
                    FileVO fv = new FileVO();
                    String fileKey = objectSummary.getKey();
                    String fileType = fileKey.substring(fileKey.lastIndexOf(".") + 1, fileKey.length()).toLowerCase();
                    if (Arrays.asList(IMAGE_TYPE_LIST).contains(fileType)) {
                        fv.setType("image");
                    } else if (Arrays.asList(VIDEO_TYPE_LIST).contains(fileType)) {
                        fv.setType("video");
                    } else {
                        fv.setType("other");
                    }
                    fv.setName(fileKey.substring(fileKey.lastIndexOf("/")+1));
                    fv.setKey(fileKey);
                    fv.setLastModified(objectSummary.getLastModified());
                    float sizeKb= (float)objectSummary.getSize()/(float)1024;
                    DecimalFormat df = new DecimalFormat("0.000");
                    if(sizeKb <= 1024) {
                        fv.setSize(df.format(sizeKb) + "KB");
                    }else{
                        fv.setSize(df.format(sizeKb/1024) + "MB");
                    }
                    listRe.add(fv);
                }
            }while (objectListing.isTruncated());

            return listRe;
        } catch (Exception ex) {
            logger.info("==========>查询列表失败", ex);
            return new ArrayList<FileVO>();
        }
    }

    private String switchFileType(String fileUrl,String fileType){
        String dateStr = format.format(new Date());
        if (Arrays.asList(IMAGE_TYPE_LIST).contains(fileType)) {
            fileUrl += "images/";
        } else if (Arrays.asList(VIDEO_TYPE_LIST).contains(fileType)) {
            fileUrl += "videos/";
        } else if (Arrays.asList(DOCUMENT_TYPE_LIST).contains(fileType)) {
            fileUrl += "docs/";
        } else{
            fileUrl += "files/";
        }
        fileUrl += dateStr + "/" + UUID.randomUUID().toString().replace("-", "") + "." + fileType;
        return fileUrl;
    }

    private ObjectMetadata getAliyunObjectMetadata(String fileType){
        ObjectMetadata objectMetadata = new ObjectMetadata();
        switch (fileType) {
            case "bmp":
                objectMetadata.setContentType("application/x-bmp");
                break;
            case "jpg":
                objectMetadata.setContentType("image/jpeg");
                break;
            case "jpeg":
                objectMetadata.setContentType("image/jpeg");
                break;
            case "png":
                objectMetadata.setContentType("image/png");
                break;
            case "tiff":
                objectMetadata.setContentType("image/tiff");
                break;
            case "gif":
                objectMetadata.setContentType("image/gif");
                break;
            case "pcx":
                objectMetadata.setContentType("application/x-pcx");
                break;
            case "tga":
                objectMetadata.setContentType("application/x-tga");
                break;
            case "svg":
                objectMetadata.setContentType("text/xml");
                break;
            case "cdr":
                objectMetadata.setContentType("application/x-cdr");
                break;
            case "dxf":
                objectMetadata.setContentType("application/x-dxf");
                break;
            case "eps":
                objectMetadata.setContentType("application/x-ps");
                break;
            case "ai":
                objectMetadata.setContentType("application/postscript");
                break;
            case "mpeg":
                objectMetadata.setContentType("video/mpg");
                break;
            case "mpg":
                objectMetadata.setContentType("video/mpg");
                break;
            case "mpv":
                objectMetadata.setContentType("video/mpg");
                break;
            case "mpv2":
                objectMetadata.setContentType("video/mpeg");
                break;
            case "mpe":
                objectMetadata.setContentType("video/x-mpeg");
                break;
            case "mpga":
                objectMetadata.setContentType("audio/rn-mpeg");
                break;
            case "wmf":
                objectMetadata.setContentType("application/x-wmf");
                break;
            case "mp4":
                objectMetadata.setContentType("video/mpeg4");
                break;
            case "avi":
                objectMetadata.setContentType("video/avi");
                break;
            case "wmv":
                objectMetadata.setContentType("video/x-ms-wmv");
                break;
            case "asf":
                objectMetadata.setContentType("video/x-ms-asf");
                break;
            case "rmvb":
                objectMetadata.setContentType("application/vnd.rn-realmedia-vbr");
                break;
            case "txt":
                objectMetadata.setContentType("text/plain");
                break;
            case "doc":
                objectMetadata.setContentType("application/msword");
                break;
            case "docx":
                objectMetadata.setContentType("application/msword");
                break;
            case "xls":
                objectMetadata.setContentType("application/vnd.ms-excel");
                break;
            case "htm":
                objectMetadata.setContentType("text/html");
                break;
            case "html":
                objectMetadata.setContentType("text/html");
                break;
            case "jsp":
                objectMetadata.setContentType("text/html");
                break;
            case "rtf":
                objectMetadata.setContentType("application/msword");
                break;
            case "wpd":
                objectMetadata.setContentType("application/x-wpd");
                break;
            case "pdf":
                objectMetadata.setContentType("application/pdf");
                break;
            case "ppt":
                objectMetadata.setContentType("application/vnd.ms-powerpoint");
                break;
            default:
                objectMetadata.setContentType("application/octet-stream");
        }
        return objectMetadata;
    }
}
